<?php

class Social_User extends Social_Base
{
    private $_sessionSecret = 'NTmQE38SMLh4QX4K';

    public function init()
    {
        $this->setTableName('users');
        $this->setPrimaryKey('user_id');
    }

    public function getUser($userId, $fields = array())
    {
        $user = $this->getByPrimary($userId, $fields);
        if(empty($user)) {
            throw new InvalidArgumentException('User not found');
        }
        return $user;
    }

    public function findUsers()
    {
        return $this->select()->from()->where('user_id', '=', $userId)
            ->orderBy()->byPage($currentPage);
    }

    public function addUser($username, $password, $email)
    {
        $currentTime = date('Y-m-d H:i:s');
        $salt = $this->_generatePasswordSalt();
        $data = array();
        $data['username'] = $username;
        $data['password'] = $this->_encodePassword($password, $salt);
        $data['salt'] = $salt;
        $data['created_at'] = $currentTime;
        return $this->insert($data);
    }

    public function login($username = null, $password = null)
    {
        // Validate argument
        $this->_validateUsername($username);
        $this->_validatePassword($password);

        // Get user record
        $user = $this->get(array('username' => $username));
        if(empty($user)) {
            throw new InvalidArgumentException('Username does not exists');
        }

        if($user['password'] != $this->_encodePassword($password, $user['salt'])) {
            throw new InvalidArgumentException('Password wrong.');
        }

        // Generate session key
        $sessionString = md5($username . $user['password']) . $user['user_id'];
        $crypt = new System_Crypt_Rc4($this->_sessionSecret);
        return $crypt->encrypt($sessionString);
    }

    public function auth($sessionKey)
    {
        $user = $this->_getBySessionKey($sessionKey);
        $requiredFields = array('username', 'email');
        $userInfo = array();
        foreach($requiredFields as $requiredField) {
            $userInfo[$requiredField] = isset($user[$requiredField]) ? $user[$requiredField] : null;
        }
        return $userInfo;
    }

    public function password($sessionKey, $oldPassword, $newPassword, $confirmPassword)
    {
        $this->_validatePassword($newPassword);
        if($newPassword != $confirmPassword) {
            throw new InvalidArgumentException('Confirm password wrong');
        }
        if(empty($oldPassword)) {
            throw new InvalidArgumentException('Old password required');
        }

        $user = $this->_getBySessionKey($sessionKey);

        // Validate old password
        if($user['password'] != $this->_encodePassword($oldPassword, $user['salt'])) {
            throw new InvalidArgumentException('Old password wrong');
        }

        // Generate password
        $salt = $this->_generatePasswordSalt();
        $password = $this->_encodePassword($newPassword, $salt);
        $data = array('password' => $password, 'salt' => $salt);
        $this->updateByPrimary($user['user_id'], $data);
        return true;
    }

    public function register($username, $password, $email)
    {
        // Validate argument
        $this->_validateUsername($username);
        $this->_validatePassword($password);
        $this->_validateEmail($email);

        if($this->exists(array('username' => $username))) {
            throw new InvalidArgumentException('Username exists');
        }

        if($this->exists(array('email' => $email))) {
            throw new InvalidArgumentException('Email exists');
        }

        // Generate password
        $salt = $this->_generatePasswordSalt();
        $password = $this->_encodePassword($password, $salt);

        // Save user
        $currentTime = date('Y-m-d H:i:s');
        $userData = array();
        $userData['username'] = $username;
        $userData['password'] = $password;
        $userData['salt'] = $salt;
        $userData['email'] = $email;
        $userData['created_at'] = $currentTime;
        $userData['updated_at'] = $currentTime;
        return $this->create($userData);
    }

    public function email($sessionKey, $newEmail)
    {
        $user = $this->_getBySessionKey($sessionKey);

        $this->_validateEmail($email);
        $data = array('email' => $email);
        $this->updateByPrimary($user['user_id'], $data);
        return true;
    }

    private function _getBySessionKey($sessionKey)
    {
        if(empty($sessionKey)) {
            throw new InvalidArgumentException('Session key required', 1000);
        }

        // Decrypt session key
        $crypt = new System_Crypt_Rc4($this->_sessionSecret);
        $sessionString = $crypt->decrypt($sessionKey);

        $userId = substr($sessionString, 32);
        if(empty($userId)) {
            throw new InvalidArgumentException('Invalid session key', 1001);
        }
        $sessionToken = substr($sessionString, 0, 32);

        // Get user
        $user = $this->getByPrimary($userId);
        if(empty($user)) {
            throw new InvalidArgumentException('User not found', 1002);
        }

        // Validate token
        if($sessionToken != md5($user['username'] . $user['password'])) {
            throw new InvalidArgumentException('Invalid session key',1003);
        }
        return $user;
    }

    private function _generatePasswordSalt()
    {
        return md5(mt_rand() . time());
    }

    private function _encodePassword($rawString, $salt)
    {
        return md5(md5($rawString) . $salt);
    }

    private function _validateEmail($email)
    {
        $this->validate($email, 'email', 'required', null, 'Email required');
        $this->validate($email, 'email', 'email', null, 'Email is not valid.');
    }

    private function _validateUsername($username)
    {
        $this->validate($username, 'username', 'required', null, 'Username required');
        $this->validate($username, 'username', 'minLength', 3, 'Username must be 3 characters more.');
        $this->validate($username, 'username', 'maxLength', 30, 'Username must be 30 characters or less.');
        $this->validate($username, 'username', 'username', null, 'Invalid username(a-zA-Z0-9)');
    }

    private function _validatePassword($password)
    {
        $this->validate($password, 'password', 'required', null, 'Password required');
        $this->validate($password, 'password', 'minLength', 3, 'Password min length');
        $this->validate($password, 'password', 'maxLength', 30, 'Password max length');
    }

}
